﻿#include "form_testing.h"
#include "ui_form_testing.h"

// add necessary includes here
#include "utils/database/database_bll_t1c.h"
#include "utils/algorithm/algorithm_bll_t1c.h"
#include "utils/can/can_bll_t1c.h"
#include "utils/firmware/firmware_bll.h"
#include "utils/configure/configure_bll.h"
#include "ui/share/form_progressbar.h"
#include "ui/share/form_prompt_dlg.h"
#include "ui/share/form_inquiry_dlg.h"
#include "ui/share/form_shield_dlg.h"
#include "ui/share/my_switch_button.h"
#include "ui/home/homepage/form_view_procedure.h"
#include "ui/home/homepage/form_chart_info.h"
#include "ui/factory/extract_debug/mvc/extract_data_define.h"

extern QMultiMap<EUM_TASK_TYPE, unsigned int> g_mapTestTaskRunning;
extern int g_nKitType;

static QList<ST_STEP_INFO> gs_lstFlowModelData;
static QList<ST_PCR_PARAMS> gs_lstPcrParams;
static QQueue<ST_FLOW_PROC_ACTION> gs_queFlowProcAction;
static QQueue<ST_PCR_PROC_ACTION> gs_quePcrProcAction;

int gs_nBtnTestState;
int gs_nBtnTrayState;
int gs_nBtnResultState;

QString gs_sTestingName;
QString gs_sStepProgress;
QString gs_sStepName;
QString gs_sHoleNumber;
QString gs_sStepDetail;
QString gs_sStillTime;
QString gs_sMixingTime;
QString gs_sMagnetTime;
QString gs_sStillTime2;
QString gs_sMixingTime2;
QString gs_sMagnetTime2;
QString gs_sTempValHole;
QString gs_sTempValHole1;
QString gs_sTempValHole2;
QString gs_sTempValHole3;
QString gs_sTempValHole4;

enum E_BTN_TEST_STATE { BTS_NEW, BTS_START, BTS_STOP };
enum E_BTN_TRAY_STATE { BTS_EJECT, BTS_INJECT };
enum E_BTN_RESULT_STATE { BRS_NONE, BRS_COMPLITED, BRS_CANCELED };

/*
 * Function:    FormTesting
 * Description: The default constructor
 * Paramer:     void
 * Return:
 */
FormTesting::FormTesting(QWidget *parent) :
    QWidget(parent)
  , ui(new Ui::FormTesting)
{
    qDebug() << Q_FUNC_INFO;
    ui->setupUi(this);

    gs_lstFlowModelData.clear();
    gs_lstPcrParams.clear();
    gs_queFlowProcAction.clear();
    gs_quePcrProcAction.clear();
}

/*
 * Function:    ~FormTesting
 * Description: The default destructor
 * Paramer:     void
 * Return:
 */
FormTesting::~FormTesting()
{
    qDebug() << Q_FUNC_INFO;
    delete ui;

    if(m_pCancelTesting->isActive()) {
        m_pCancelTesting->stop();
    }

    if(m_pFrmProgressbar) {
        delete m_pFrmProgressbar;
        m_pFrmProgressbar = nullptr;
    }

    if(m_pReadTemperature->isActive()) {
        m_pReadTemperature->stop();
        delete m_pReadTemperature;
        m_pReadTemperature = nullptr;
    }

    if(m_pCancelTesting->isActive()) {
        m_pCancelTesting->stop();
        delete m_pCancelTesting;
        m_pCancelTesting = nullptr;
    }

    if(m_pStepCountdown->isActive()) {
        m_pStepCountdown->stop();
        delete m_pStepCountdown;
        m_pStepCountdown = nullptr;
    }
}

/*
 * Function:    getInstance
 * Description: Get an instance of "FormTesting" class, if exist, return it
 * Paramer:     QWidget *pParent - the pointer of QWidget class
 * Return:      the pointer of "FormTesting" class
 */
FormTesting* FormTesting::getInstance(QWidget *pParent)
{
    static FormTesting *pNew = nullptr;
    if(!pNew) {
        // Create a new object if it doesn't exist
        pNew = new FormTesting(pParent);
        if(nullptr == pNew) return nullptr;

        // After constructing object, initialize the object
        pNew->_initialze();

        // Init the window style
        pNew->_initUi();

        // Hide widget firstly
//        pNew->hide();
    }

    // Set the parent object
    if(pParent) pNew->setParent(pParent);

    return pNew;
}

/*
 * Function:    _initialze
 * Description: After constructing object, initialize the object
 * Paramer:     void
 * Return:      void
 */
void FormTesting::_initialze()
{
    gs_nBtnTestState = BTS_NEW;
    gs_nBtnTrayState = BTS_EJECT;
    gs_nBtnResultState = BRS_NONE;

    gs_sTestingName = "";
    gs_sStepProgress = "";
    gs_sStepName = "";
    gs_sHoleNumber = "";
    gs_sStepDetail = "";
    gs_sStillTime = "";
    gs_sMixingTime = "";
    gs_sMagnetTime = "";
    gs_sStillTime2 = "";
    gs_sMixingTime2 = "";
    gs_sMagnetTime2 = "";
    gs_sTempValHole = "";
    gs_sTempValHole1 = "";
    gs_sTempValHole2 = "";
    gs_sTempValHole3 = "";
    gs_sTempValHole4 = "";

    // Init an object
    if(!m_pFrmViewProcedure) m_pFrmViewProcedure = FormViewProcedure::getInstance(this);
    Q_ASSERT(m_pFrmViewProcedure);

    if(!m_pFrmProgressbar) m_pFrmProgressbar = new FormProgressbar(ui->widget_TestingProgress);
    Q_ASSERT(m_pFrmProgressbar);

    if(!m_pReadTemperature) m_pReadTemperature = new QTimer(this);
    Q_ASSERT(m_pReadTemperature);
    m_pReadTemperature->setInterval(DEF_SEC_INTERVAL);

    if(!m_pStepCountdown) m_pStepCountdown = new QTimer(this);
    Q_ASSERT(m_pStepCountdown);
    m_pStepCountdown->setInterval(DEF_SEC_INTERVAL);

    if(!m_pCancelTesting) m_pCancelTesting = new QTimer(this);
    Q_ASSERT(m_pCancelTesting);

    if(!m_pImageSample) m_pImageSample = new QImage();
    if(!m_pImageExtract) m_pImageExtract = new QImage();

    // Signal-slot initialization
    std::vector<bool> vecConnect;
    vecConnect.push_back(QObject::connect(this, SIGNAL(signalRefreshUI(bool)), this, SLOT(slotRefreshUI(bool)), static_cast<Qt::ConnectionType>(Qt::AutoConnection | Qt::UniqueConnection)));
    vecConnect.push_back(QObject::connect(this, SIGNAL(signalRefreshProcedure()), this, SLOT(slotRefreshProcedure()), static_cast<Qt::ConnectionType>(Qt::AutoConnection | Qt::UniqueConnection)));
    vecConnect.push_back(QObject::connect(this, SIGNAL(signalDrawHolePosition(int, int, int, int, QColor)), this, SLOT(slotDrawHolePosition(int, int, int, int, QColor)), static_cast<Qt::ConnectionType>(Qt::AutoConnection | Qt::UniqueConnection)));
    vecConnect.push_back(QObject::connect(m_pStepCountdown, SIGNAL(timeout()), this, SLOT(slotShowCountdown()), static_cast<Qt::ConnectionType>(Qt::AutoConnection | Qt::UniqueConnection)));
    vecConnect.push_back(QObject::connect(m_pCancelTesting, SIGNAL(timeout()), this, SLOT(slotCancelTesting()), static_cast<Qt::ConnectionType>(Qt::AutoConnection | Qt::UniqueConnection)));
    vecConnect.push_back(QObject::connect(ui->listView_Procedure, SIGNAL(doubleClicked(const QModelIndex &)), ui->pushButton_ViewProcedure, SIGNAL(clicked())));
    vecConnect.push_back(QObject::connect(ui->listView_Procedure_2, SIGNAL(doubleClicked(const QModelIndex &)), ui->pushButton_ViewProcedure, SIGNAL(clicked())));
    vecConnect.push_back(QObject::connect(m_pReadTemperature, SIGNAL(timeout()), this, SLOT(slotReadTemperature()), static_cast<Qt::ConnectionType>(Qt::AutoConnection | Qt::UniqueConnection)));
    for(int i=0; i<(int)vecConnect.size(); i++) {
        Q_ASSERT(vecConnect.at(i));
    }
}

/*
* Function:		_initUi
* Description:	Init the window style
* Paramer:		void
* Return:		void
*/
void FormTesting::_initUi()
{
    // Displays the background image of the window
    QWidget::setAttribute(Qt::WA_StyledBackground);

    SET_STYLE_SHEET(QString(":/%1/home/homepage/%2.qss").arg("skin_1").arg(this->objectName()));

//    ui->label_Animation_Reaction->setPixmap(QPixmap::fromImage(*m_pImageReaction).scaled(ui->label_Animation_Reaction->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
//    ui->label_Animation_Extract->setPixmap(QPixmap::fromImage(*m_pImageExtract).scaled(ui->label_Animation_Extract->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
//    ui->label_Animation_Sample->setPixmap(QPixmap::fromImage(*m_pImageSample).scaled(ui->label_Animation_Sample->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation));

    if(m_pImageSample) m_pImageSample->load(":/images/home/testing/sample.png");
    ui->label_Animation_Sample->setPixmap(QPixmap::fromImage(*m_pImageSample).scaled(ui->label_Animation_Sample->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation));

    ui->tabWidget_KitType->setCurrentIndex(1);

    // Load the images of testing status
    _loadExtractImage(E_EK_T1C_HOLE_11);

    ui->progressBar->setFormat("%v/%m");

    // Initalize labels
    _refreshTestingInfoOnLabels(0);

    ui->label_TestingInfo->setText("");
    ui->stackedWidget_Info->setCurrentIndex(0);
    ui->pushButton_ViewProcedure->setVisible(false);
    ui->label_StepDetail_2->setAlignment(Qt::AlignHCenter);
    ui->label_Tray->setVisible(false);
}

/*
* Function:		slotRefreshUI
* Description:	The slot that refresh UI info
* Paramer:		bool bInit - first or not
* Return:		void
*/
void FormTesting::slotRefreshUI(bool bInit)
{
    qDebug() << Q_FUNC_INFO << bInit;

    if(bInit) {
        gs_nBtnTestState = BTS_NEW;

        ui->label_TestingInfo->setText("");
        ui->pushButton_NewTest->setText(tr("New Test"));
        ui->pushButton_Return->setVisible(false);
        ui->pushButton_ViewProcedure->setVisible(false);
        ui->stackedWidget_Info->setCurrentIndex(1);
    }
    else {
        QList<QLabel*> lstLabels = QList<QLabel*>() << ui->label_HoleTemperature << ui->label_HoleTemperature_2
                                                    << ui->label_Hole1 << ui->label_Hole1_2
                                                    << ui->label_Hole2 << ui->label_Hole2_2
                                                    << ui->label_Hole3 << ui->label_Hole3_2
                                                    << ui->label_Hole4 << ui->label_Hole4_2
                                                    << ui->label_StepDetail_2;

        foreach(auto var, lstLabels) {
            var->setVisible(false);
        }

        // Get the login mode
        ST_CFG_LOGIN_MODE stLoginMode;
        ConfigureBLL::getInstance(this)->loadLoginMode(stLoginMode);

        switch ((int)stLoginMode.eumLoginMode) {
        case E_MODE_GUEST:
            ui->label_HoleTemperature->setVisible(true);
            ui->label_HoleTemperature_2->setVisible(true);
            break;
        case E_MODE_ADMIN:
        case E_MODE_FACTORY:
            foreach(auto var, lstLabels) {
                var->setVisible(true);
            }
            ui->label_HoleTemperature->setVisible(false);
            ui->label_HoleTemperature_2->setVisible(false);

            break;
        default: break;
        }
    }
}

/*
* Function:		slotRefreshProcedure
* Description:	The slot that refresh UI info
* Paramer:		void
* Return:		void
*/
void FormTesting::slotRefreshProcedure()
{
    qDebug() << Q_FUNC_INFO;

    _refreshListView();
}

/*
* Function:		changeEvent
* Description:	Triggers an event before it is switch language
* Paramer:		QShowEvent *e - the pointer of "QEvent"
* Return:		void
*/
void FormTesting::changeEvent(QEvent *e)
{
    switch (e->type()) {
    case QEvent::LanguageChange:
    {
        qDebug() << Q_FUNC_INFO;
        ui->retranslateUi(this);

        switch (gs_nBtnTestState) {
        case BTS_NEW: { ui->pushButton_NewTest->setText(tr("New Test")); break; }
        case BTS_START: { ui->pushButton_NewTest->setText(tr("Start Test")); break; }
        case BTS_STOP: { ui->pushButton_NewTest->setText(tr("Stop Test")); break; }
        default:break;
        }

        switch (gs_nBtnTrayState) {
        case BTS_EJECT: { ui->pushButton_SwitchTray->setText(tr("Eject")); break; }
        case BTS_INJECT: { ui->pushButton_SwitchTray->setText(tr("Inject")); break; }
        default:break;
        }

        switch (gs_nBtnResultState) {
        case BRS_COMPLITED: { ui->pushButton_ResultInfo->setText(tr("Test completed")); break; }
        case BRS_CANCELED: { ui->pushButton_ResultInfo->setText(tr("Test canceled")); break; }
        default: { ui->pushButton_ResultInfo->setText(""); break; }
        }

        ui->label_TestingProgress->setText(m_sProcedureName.isEmpty() ? "" : tr("Testing Name") + ": " + m_sProcedureName);
        ui->label_StepProgress_2->setText(gs_sStepProgress);
        ui->label_StepName_2->setText(gs_sStepName);
        ui->label_StepDetail_2->setText(gs_sStepDetail);
        ui->label_HoleNumber_2->setText(gs_sHoleNumber);
        ui->label_StillTime_2->setText(gs_sStillTime);
        ui->label_StillTime_3->setText(gs_sStillTime2);
        ui->label_MixingTime_2->setText(gs_sMixingTime);
        ui->label_MixingTime_3->setText(gs_sMixingTime2);
        ui->label_MagnetTime_2->setText(gs_sMagnetTime);
        ui->label_MagnetTime_3->setText(gs_sMagnetTime2);
        ui->label_HoleTemperature_2->setText(gs_sTempValHole);
        ui->label_Hole1_2->setText(gs_sTempValHole1);
        ui->label_Hole2_2->setText(gs_sTempValHole2);
        ui->label_Hole3_2->setText(gs_sTempValHole3);
        ui->label_Hole4_2->setText(gs_sTempValHole4);

        break;
    }
    default:
        break;
    }

    QWidget::changeEvent(e);
}

void FormTesting::on_pushButton_SwitchTray_clicked()
{
    FormShieldDlg::getInstance(this->parentWidget())->show();

    if(tr("Eject") == ui->pushButton_SwitchTray->text()) {
        gs_nBtnTrayState = BTS_INJECT;

        ui->pushButton_SwitchTray->setText(tr("Inject"));
        ui->pushButton_SwitchTray->setChecked(true);

        ui->stackedWidget_Progress->setCurrentIndex(0);
        ui->label_StepDetail_2->setText("");

        int nErrCode = CanBLL_T1C::getInstance(this)->switchTray(true);
        if(0 != nErrCode) {
            FormPromptWidget::getInstance(this->parentWidget())->setPromptInfo(tr("Prompt"), tr("Failed to eject the tray") + QString("[%1]").arg(nErrCode));
        }
    }
    else {
        gs_nBtnTrayState = BTS_EJECT;

        ui->pushButton_SwitchTray->setText(tr("Eject"));
        ui->pushButton_SwitchTray->setChecked(false);

        int nErrCode = CanBLL_T1C::getInstance(this)->switchTray(false);
        switch (nErrCode) {
        case 0:
            g_nKitType = 0;
            FormPromptWidget::getInstance(this->parentWidget())->setPromptInfo(tr("Prompt"), tr("Failed to close the tray") + QString("[%1]").arg(nErrCode));
            break;
        case 1:
        case 2:
            g_nKitType = nErrCode;
            break;
        case -101:
        case -102:
        case -201:
        case -202:
        case -203:
        case -204:
            g_nKitType = nErrCode;
            break;
        default:
            break;
        }
    }

    FormShieldDlg::getInstance(this->parentWidget())->hide();
    qDebug() << Q_FUNC_INFO << g_nKitType;
}

void FormTesting::on_pushButton_NewTest_clicked()
{
    // Flag some holes on sample area acorrding to sample info
    if(false) {
        m_nSamplePosFlags = 0xff;
        static int nCnt = 0;
        QColor clrHole(5, 131, 209);

        if(0 == nCnt++ % 2) {
            clrHole.setRed(5);
            clrHole.setGreen(131);
            clrHole.setBlue(209);
        }
        else {
            clrHole.setRed(255);
            clrHole.setGreen(255);
            clrHole.setBlue(255);
        }

        emit signalDrawHolePosition(E_AT_SPECIMEN, 0, 0, m_nSamplePosFlags, clrHole);

        _loadExtractImage(0);
        for(int i=0; i<10; i++) {
            emit signalDrawHolePosition(E_AT_EXTRACT, 0, i, m_nSamplePosFlags, clrHole);
        }

        return;
    }

    if(tr("New Test") == ui->pushButton_NewTest->text()) {
        gs_nBtnTestState = BTS_START;
        m_nCurProcedureId = 0;
        ui->stackedWidget_Info->setCurrentIndex(0);

        ui->pushButton_NewTest->setText(tr("Start Test"));
        ui->pushButton_Return->setVisible(true);
        ui->pushButton_ViewProcedure->setVisible(true);

        _refreshListView();
    }
    else if(tr("Start Test") == ui->pushButton_NewTest->text()) {
        // Prevent the button from clicking repeatedly quickly
        FormShieldDlg::getInstance(this)->show();

        // Startup a testing
        _startupTest();

        FormShieldDlg::getInstance(this)->hide();
    }
    else {
        // Show the inquiry dialog
        QString sPromptInfo = tr("Are you sure to stop testing?");

        FormInquiryWidget::getInstance(this)->setInquiryInfo(sPromptInfo, E_WA_FAC_SAMPLE_TEST_STOP);
        // Wait for the slotRespondInfoFromDlg function to respond
    }
}

void FormTesting::on_pushButton_Return_clicked()
{
    gs_nBtnTestState = BTS_NEW;
    m_nCurProcedureId = 0;
    ui->stackedWidget_Info->setCurrentIndex(1);

    ui->pushButton_NewTest->setText(tr("New Test"));
    ui->pushButton_Return->setVisible(false);
    ui->pushButton_ViewProcedure->setVisible(false);

    _refreshTestingInfoOnLabels(0);
}

void FormTesting::on_pushButton_ViewProcedure_clicked()
{
    if(0 < m_nCurProcedureId) {
        m_pFrmViewProcedure->displayWindow(E_PT_EXTRACT, m_nCurProcedureId);
    }
    else {
        FormPromptWidget::getInstance(this->parentWidget())->setPromptInfo(tr("Prompt"), tr("Please select a procedure file"));
    }
}

void FormTesting::on_pushButton_ResultInfo_clicked()
{
    ui->stackedWidget_Progress->setCurrentIndex(0);
    ui->label_StepDetail_2->setText("");
}

void FormTesting::on_listView_Procedure_clicked(const QModelIndex &index)
{
    m_nCurProcedureId = ((QStandardItemModel*)(ui->listView_Procedure->model()))->itemFromIndex(index)->statusTip().trimmed().toInt();
}

void FormTesting::on_listView_Procedure_2_clicked(const QModelIndex &index)
{
    m_nCurProcedureId = ((QStandardItemModel*)(ui->listView_Procedure_2->model()))->itemFromIndex(index)->statusTip().trimmed().toInt();
}

void FormTesting::on_tabWidget_KitType_currentChanged(int index)
{
    qDebug() << Q_FUNC_INFO << index;

    switch (index) {
    case 1:
        _loadExtractImage(E_EK_T1C_HOLE_11);
        break;
    default:
        _loadExtractImage(E_EK_T1C_HOLE_10);
        break;
    }
}

void FormTesting::slotRespondInfoFromDlg(int nReplyType, bool bAccepted)
{
    qDebug() << Q_FUNC_INFO << nReplyType << bAccepted;

    if(bAccepted) {
        switch (nReplyType) {
        case E_WA_FAC_SAMPLE_TEST_STOP:
        {
            // Prevent the button from clicking repeatedly quickly
            FormShieldDlg::getInstance(this)->show();

            // Stop testing
            _stopTest();

            FormShieldDlg::getInstance(this)->hide();

            break;
        }
        default: break;
        }
    }
}

void FormTesting::slotRespondInfoFromDlg(int nReplyType, bool bAccepted, QVariant varProcessParams)
{
    qDebug() << Q_FUNC_INFO << nReplyType << bAccepted << varProcessParams;
}

void FormTesting::slotProcessTestCycles(unsigned int unCanId, ST_FLOW_PROC_ACTION stFlowProcAction)
{
    qDebug() << Q_FUNC_INFO << unCanId << stFlowProcAction.nCmdID << stFlowProcAction.nExceptCmdID << stFlowProcAction.nIdx << stFlowProcAction.nStepIdx;

    static int s_nLastHoleIdx = -1;

    switch (stFlowProcAction.nCmdID) {
    case E_TP_STOP:
        qDebug() << Q_FUNC_INFO << m_nIapTesting;

        if(0 < stFlowProcAction.nExceptCmdID) {
            QString sCmdText = UtilityAPI::getInstance()->convertCmdId2Text(stFlowProcAction.nExceptCmdID);
            this->slotRespondInfoFromDlg(E_WA_FAC_SAMPLE_TEST_STOP, true);

            QString sException = QString("%1[%2], %3").arg(tr("Catch an exception")).arg(sCmdText).arg(tr("testing is cancelled"));
            FormPromptWidget::getInstance(this)->setPromptInfo(tr("Prompt"), sException);
            return;
        }

        if(0 < m_nIapTesting) {
            qint64 nTimeConsume = m_tTimeStamp.secsTo(QDateTime::currentDateTime()) - 3;
            qDebug() << Q_FUNC_INFO << unCanId << stFlowProcAction.nCmdID << nTimeConsume;

            if(0 < m_nExtractId && 0 < nTimeConsume) {
                ST_PROCESS_PARAMS stProcessParamsExt;
                DatabaseBLL_T1C::getInstance(this)->selectProcessParamsById(stProcessParamsExt, m_nExtractId, E_PT_EXTRACT);
                if(0 < stProcessParamsExt.nProcessId) {
                    stProcessParamsExt.nTimeConsume = nTimeConsume;

                    // Update time consuming
                    DatabaseBLL_T1C::getInstance(this)->updateProcessParamsById(stProcessParamsExt, stProcessParamsExt.nProcessId);
                }
            }
        }

        s_nLastHoleIdx = -1;
        m_nIapTesting = 0;

        // Complete extract testing
        _completeTesting(false);

        if(m_pCancelTesting->isActive()) {
            m_pCancelTesting->stop();
            m_pCancelTesting->start(DEF_SEC_INTERVAL);
        }
        break;
    default:
        if(0 <= stFlowProcAction.nStepIdx && 0 <= stFlowProcAction.nIdx) {
            // Update items info & progress
            if(1 == 1) {
                int nKitType = ui->tabWidget_KitType->currentIndex();

                QString sStepName = "";
                if(stFlowProcAction.nStepIdx <= gs_lstFlowModelData.size()){
                        QString sStepFlag = gs_lstFlowModelData.at(stFlowProcAction.nStepIdx).sStepName;
                        sStepName = UtilityAPI::getInstance()->convertStepFlag2Name(sStepFlag);
                }

                QString sCmdText = UtilityAPI::getInstance()->convertCmdId2Text(stFlowProcAction.nCmdID);

                QString sCycleInfo = QString("%1/%2")
                        .arg(stFlowProcAction.nStepIdx + 1)
                        .arg(m_nFlowCycleCnt);

                gs_sStepProgress = sCycleInfo;
                gs_sStepName = sStepName;
                gs_sStepDetail = sCmdText;

                ui->label_StepProgress_2->setText(sCycleInfo);
                ui->label_StepName_2->setText(sStepName);
                ui->label_StepDetail_2->setText(sCmdText);
                ui->progressBar->setValue(stFlowProcAction.nIdx + 1);

                if(0 < m_nIapTesting) {
                    foreach(auto item, gs_lstFlowModelData) {
                        if(stFlowProcAction.nStepIdx == item.nStepIdx) {
                            QString sHoleNumber = (0 < item.nHoleNumber) ? QString::number(item.nHoleNumber) : "";
                            QString sStillTime = (0 < item.nSilentDuration) ? QString::number(item.nSilentDuration) : "0";
                            QString sMixingTime = (0 < item.nMixingDuration) ? QString::number(item.nMixingDuration) : "0";
                            QString sMagnetTime = (0 < item.nMagnetDuration) ? QString::number(item.nMagnetDuration) : "0";

                            sStillTime = QString("%1 %2").arg(sStillTime).arg(tr("sec"));
                            sMixingTime = QString("%1 %2").arg(sMixingTime).arg(tr("sec"));
                            sMagnetTime = QString("%1 %2").arg(sMagnetTime).arg(tr("sec"));

                            gs_sHoleNumber = sHoleNumber;
                            gs_sStillTime = sStillTime;
                            gs_sMixingTime = sMixingTime;
                            gs_sMagnetTime = sMagnetTime;

                            ui->label_HoleNumber_2->setText(sHoleNumber);
                            ui->label_StillTime_2->setText(sStillTime);
                            ui->label_MixingTime_2->setText(sMixingTime);
                            ui->label_MagnetTime_2->setText(sMagnetTime);

                            m_nCurRowPosIdx = item.nHoleNumber - 1;

                            if(DF_AREA_EXTRACT == item.sHoleType && 0 <= m_nCurRowPosIdx) {
                                if(s_nLastHoleIdx != m_nCurRowPosIdx) {
                                    s_nLastHoleIdx = m_nCurRowPosIdx;

                                    // Reset the images
                                    _resetImages(ui->tabWidget_KitType->currentIndex());

                                    // Draw hole position by painter
                                    if(DF_NAME_TIP_SUCTTION == item.sStepName || DF_NAME_TIP_SPIT == item.sStepName || DF_NAME_TIP_MIXING == item.sStepName) {
                                        emit signalDrawHolePosition(E_AT_EXTRACT, nKitType, m_nCurRowPosIdx, m_nSamplePosFlags, QColor(0, 255, 0));
                                    }
                                    else {
                                        emit signalDrawHolePosition(E_AT_EXTRACT, nKitType, m_nCurRowPosIdx, m_nSamplePosFlags, QColor(5, 131, 209));
                                    }

                                    gs_sTempValHole1 = "";
                                    gs_sTempValHole2 = "";
                                    gs_sTempValHole3 = "";
                                    gs_sTempValHole4 = "";
                                    ui->label_Hole1_2->setText(gs_sTempValHole1);
                                    ui->label_Hole2_2->setText(gs_sTempValHole2);
                                    ui->label_Hole3_2->setText(gs_sTempValHole3);
                                    ui->label_Hole4_2->setText(gs_sTempValHole4);
                                }
                            }
                            else if(DF_AREA_SPECIMEN == item.sHoleType && 0 <= m_nCurRowPosIdx) {
                                s_nLastHoleIdx = -1;

                                // Reset the images
                                _resetImages(ui->tabWidget_KitType->currentIndex());

                                // Draw hole position by painter
                                emit signalDrawHolePosition(E_AT_SPECIMEN, 0, 0, m_nSamplePosFlags, QColor(0, 255, 0));
                            }

                            break;
                        }
                    }
                }
            }

            // Shutdown for still/mixing/magnet actions
            for(int i=0; i<gs_queFlowProcAction.size(); i++) {
                // Start the timer for the next step
                if(stFlowProcAction.nIdx == i)  {
                    QString sDefaultTime = QString("%1 %2").arg(0).arg(tr("sec"));

                    // Still  duration
                    if(EM_CSA_DELAY == gs_queFlowProcAction.at(i).nCmdID) {
                        m_stStepCountdown.nCmdId = gs_queFlowProcAction.at(i).nCmdID;
                        m_stStepCountdown.nTotalTime = gs_queFlowProcAction.at(i).fParam / DEF_SEC_INTERVAL;

                        if(0 < m_stStepCountdown.nTotalTime) {
                            gs_sStillTime2 = _seconds2Time(m_stStepCountdown.nTotalTime);

                            ui->label_StillTime_2->setVisible(false);
                            ui->label_StillTime_3->setVisible(true);
                            ui->label_StillTime_3->setText(gs_sStillTime2);

                            m_pStepCountdown->start(DEF_SEC_INTERVAL);
                        }
                        else {
                            ui->label_StillTime_2->setVisible(true);
                            ui->label_StillTime_3->setVisible(false);
                        }

                        break;
                    }

                    // Mixing duration
                    if(E_TP_BUBB == gs_queFlowProcAction.at(i).nCmdID || E_TP_OMIX == gs_queFlowProcAction.at(i).nCmdID) {
                        int nDuration = 0;
                        if(E_TP_OMIX == gs_queFlowProcAction.at(i).nCmdID)
                            nDuration = gs_queFlowProcAction.at(i).fParam3;
                        else
                            nDuration = gs_queFlowProcAction.at(i).fParam2;

                        m_stStepCountdown.nCmdId = gs_queFlowProcAction.at(i).nCmdID;
                        m_stStepCountdown.nTotalTime = nDuration / DEF_SEC_INTERVAL;

                        if(0 < m_stStepCountdown.nTotalTime) {
                            gs_sMixingTime2 = _seconds2Time(m_stStepCountdown.nTotalTime);

                            ui->label_MixingTime_2->setVisible(false);
                            ui->label_MixingTime_3->setVisible(true);
                            ui->label_MixingTime_3->setText(gs_sMixingTime2);

                            m_pStepCountdown->start(DEF_SEC_INTERVAL);
                        }
                        else {
                            ui->label_MixingTime_2->setVisible(true);
                            ui->label_MixingTime_3->setVisible(false);
                        }

                        break;
                    }

                    // Magnetic duration
                    if(E_TP_LKDO == gs_queFlowProcAction.at(i).nCmdID || E_TP_OMAG == gs_queFlowProcAction.at(i).nCmdID) {
                        int nDuration = 0;
                        if(E_TP_OMAG == gs_queFlowProcAction.at(i).nCmdID)
                            nDuration = gs_queFlowProcAction.at(i).fParam4;
                        else
                            nDuration = gs_queFlowProcAction.at(i).fParam3;

                        m_stStepCountdown.nCmdId = gs_queFlowProcAction.at(i).nCmdID;
                        m_stStepCountdown.nTotalTime = nDuration / DEF_SEC_INTERVAL;

                        if(0 < m_stStepCountdown.nTotalTime) {
                            gs_sMagnetTime2 = _seconds2Time(m_stStepCountdown.nTotalTime);

                            ui->label_MagnetTime_2->setVisible(false);
                            ui->label_MagnetTime_3->setVisible(true);
                            ui->label_MagnetTime_3->setText(gs_sMagnetTime2);

                            int nMovingTime = 5 * DEF_SEC_INTERVAL;
                            if(E_TP_OMAG == gs_queFlowProcAction.at(i).nCmdID)
                                nMovingTime = 25 * DEF_SEC_INTERVAL;

                            QTimer::singleShot(nMovingTime, this, [&](){
                                m_pStepCountdown->start(DEF_SEC_INTERVAL);
                            });
                        }
                        else {
                            ui->label_MagnetTime_2->setVisible(true);
                            ui->label_MagnetTime_3->setVisible(false);
                        }

                        break;
                    }
                }
            }
        }
        break;
    }
}

void FormTesting::slotHeatValueReceived(unsigned int unCanId, QList<float> lstDataValue)
{
    qDebug() << Q_FUNC_INFO << unCanId << lstDataValue;

    if(CanVars::gs_nHeatIapCanID_CH1 == unCanId - 1) {
        // Heating for extract
        if(DEF_HEAT_CHANNEL_SIZE + 1 == lstDataValue.size()) {
            int nChannelFlag = (int)lstDataValue[0];
            QStringList lstCurTempVal;

            for(int i=0; i<DEF_HEAT_CHANNEL_SIZE; i++) {
                bool bHeatFlagCH = (0 < (nChannelFlag & (0x1 << i)));
                float fTempValCH = lstDataValue[i + 1] / float(100);

                QString sTempVal = QString("%1 %2")
                        .arg((0 < fTempValCH) ? QString::number(fTempValCH, 'f', 1) : " ---- ")
                        .arg(tr("°C"));

                if(bHeatFlagCH) {
                    sTempVal += " ☀";
                    lstCurTempVal << sTempVal;
                }

                switch (i) {
                case 0: { gs_sTempValHole1 = sTempVal; ui->label_Hole1_2->setText(sTempVal); break; }
                case 1: { gs_sTempValHole2 = sTempVal; ui->label_Hole2_2->setText(sTempVal); break; }
                case 2: { gs_sTempValHole3 = sTempVal; ui->label_Hole3_2->setText(sTempVal); break; }
                case 3: { gs_sTempValHole4 = sTempVal; ui->label_Hole4_2->setText(sTempVal); break; }
                default: { break; }
                }
            }

            gs_sTempValHole = "";
            if(0 < lstCurTempVal.size()) {
                gs_sTempValHole = lstCurTempVal.first().replace("☀", "").trimmed();
            }
            ui->label_HoleTemperature_2->setText(gs_sTempValHole);
        }
    }
}

void FormTesting::slotShowCountdown()
{
    int nKitType = ui->tabWidget_KitType->currentIndex();

    m_stStepCountdown.nTotalTime -= 1;
    if(0 >= m_stStepCountdown.nTotalTime) {
        m_pStepCountdown->stop();

        // Clear content
        switch (m_stStepCountdown.nCmdId) {
        case EM_CSA_DELAY:
            gs_sStillTime2 = "";

            ui->label_StillTime_2->setVisible(true);
            ui->label_StillTime_3->setText(gs_sStillTime2);
            break;
        case E_TP_BUBB:
        case E_TP_OMIX:
            gs_sMixingTime2 = "";

            ui->label_MixingTime_2->setVisible(true);
            ui->label_MixingTime_3->setText(gs_sMixingTime2);
            break;
        case E_TP_LKDO:
        case E_TP_OMAG:
            gs_sMagnetTime2 = "";

            ui->label_MagnetTime_2->setVisible(true);
            ui->label_MagnetTime_3->setText(gs_sMagnetTime2);
            break;
        default:
            break;
        }

        // Draw hole position by painter
        emit signalDrawHolePosition(E_AT_EXTRACT, nKitType, m_nCurRowPosIdx, m_nSamplePosFlags, QColor(5, 131, 209));
    }
    else {
        switch (m_stStepCountdown.nCmdId) {
        case EM_CSA_DELAY:
            gs_sStillTime2 = _seconds2Time(m_stStepCountdown.nTotalTime);

            ui->label_StillTime_2->setVisible(false);
            ui->label_StillTime_3->setText(gs_sStillTime2);
            break;
        case E_TP_BUBB:
        case E_TP_OMIX:
            gs_sMixingTime2 = _seconds2Time(m_stStepCountdown.nTotalTime);

            ui->label_MixingTime_2->setVisible(false);
            ui->label_MixingTime_3->setText(gs_sMixingTime2);
            break;
        case E_TP_LKDO:
        case E_TP_OMAG:
            gs_sMagnetTime2 = _seconds2Time(m_stStepCountdown.nTotalTime);

            ui->label_MagnetTime_2->setVisible(false);
            ui->label_MagnetTime_3->setText(gs_sMagnetTime2);
            break;
        default:
            break;
        }

        static bool s_bFlicker = true;
        static int s_nTimes = 0;
        if(0 == s_nTimes % 2) {
            // Draw hole position by painter
            if(s_bFlicker) {
                emit signalDrawHolePosition(E_AT_EXTRACT, nKitType, m_nCurRowPosIdx, m_nSamplePosFlags, QColor(3, 213, 255));
            }
            else {
                emit signalDrawHolePosition(E_AT_EXTRACT, nKitType, m_nCurRowPosIdx, m_nSamplePosFlags, QColor(5, 131, 209));
            }

            s_bFlicker = !s_bFlicker;
        }
        s_nTimes++;
    }
}

int FormTesting::slotDrawHolePosition(int nAreaType, int nKitType, int nRowIdx, int nColFlags, QColor clrBrush)
{
    qDebug() << Q_FUNC_INFO << nAreaType << nKitType << nRowIdx << nColFlags << clrBrush;

    // Handle very time-consuming tasks to prevent interface stuttering
    qApp->processEvents();
    QThread::msleep(20);

    int nRetVal = 0;

    if(E_AT_SPECIMEN == nAreaType) {
        if(m_pImageSample) {
            int nRowCnt = 1;
            int nColCnt = DEF_SAMPLE_HOLE_MAX_SIZE;

            int nOffsetLeft = 57;
            int nOffsetRight = 1421;
            int nOffsetTop = 27;
            int nOffsetBottom = 27;
            int nRadius = 55;
            int nDistanceH = (nOffsetRight - nOffsetLeft) / ((1 < nColCnt) ? nColCnt - 1: nColCnt);
            int nDistanceV = (nOffsetBottom - nOffsetTop) / ((1 < nRowCnt) ? nRowCnt - 1: nRowCnt);

            // Painter circles
            QPainter painter(m_pImageSample);
            painter.setBrush(QBrush(clrBrush));
            for(int i=0; i<nRowCnt; i++) {
                for(int j=0; j<nColCnt; j++) {
                    if(0 < (nColFlags & (0x1 << j))) {
                        painter.drawEllipse(nOffsetLeft + j * nDistanceH, nOffsetTop + (nRowCnt - 1 - i) * nDistanceV, nRadius, nRadius);
                    }
                }
            }

            ui->label_Animation_Sample->setPixmap(QPixmap::fromImage(*m_pImageSample).scaled(ui->label_Animation_Sample->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
        }

    }
    else if(E_AT_EXTRACT == nAreaType) {
        if(m_pImageExtract) {
            int nRowCnt = (E_EK_T1C_HOLE_10 == nKitType) ? 10 : 11;
            int nColCnt = DEF_SAMPLE_HOLE_MAX_SIZE;
            int nOffsetLeft = 59;
            int nOffsetRight = 1421;
            int nOffsetTop = 36;
            int nOffsetBottom = (E_EK_T1C_HOLE_10 == nKitType) ? 720 : 796;
            int nRadius = 56;
            int nDistanceH = (nOffsetRight - nOffsetLeft) / ((1 < nColCnt) ? nColCnt - 1: nColCnt);
            int nDistanceV = (nOffsetBottom - nOffsetTop) / ((1 < nRowCnt) ? nRowCnt - 1: nRowCnt);
            int nDistanceOffsetV = 0;

            // Painter circles
            QPainter painter(m_pImageExtract);
            painter.setBrush(QBrush(clrBrush));

            if(-1 == nRowIdx) {
                for(int i=0; i<nRowCnt; i++) {
                    nDistanceOffsetV = (0 == i) ? ((E_EK_T1C_HOLE_10 == nKitType) ? 50 : 20) : 0;

                    for(int j=0; j<nColCnt; j++) {
                        if(0 < (nColFlags & (0x1 << j))) {
                            painter.drawEllipse(nOffsetLeft + j * nDistanceH, nOffsetTop + (nRowCnt - 1 - i) * nDistanceV + nDistanceOffsetV, nRadius, nRadius);
                        }
                    }
                }
            }
            else {
                for(int i=0; i<nRowCnt; i++) {
                    if(nRowIdx == i) {
                        nDistanceOffsetV = (0 == i) ? ((E_EK_T1C_HOLE_10 == nKitType) ? 50 : 20) : 0;

                        for(int j=0; j<nColCnt; j++) {
                            if(0 < (nColFlags & (0x1 << j))) {
                                painter.drawEllipse(nOffsetLeft + j * nDistanceH, nOffsetTop + (nRowCnt - 1 - i) * nDistanceV + nDistanceOffsetV, nRadius, nRadius);
                            }
                        }

                        break;
                    }
                }
            }

            ui->label_Animation_Extract->setPixmap(QPixmap::fromImage(*m_pImageExtract).scaled(ui->label_Animation_Extract->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
        }
    }

    qDebug() << Q_FUNC_INFO << nRetVal;
    return nRetVal;
}

void FormTesting::slotCancelTesting()
{
    qDebug() << Q_FUNC_INFO;

    if(m_pCancelTesting->isActive()) m_pCancelTesting->stop();

    // Stop the progress bar
    emit m_pFrmProgressbar->signalRunTest(false, 0, E_PS_IDLE);

    FormShieldDlg::getInstance(this)->hide();
    FormPromptWidget::getInstance(this)->hide();

    // Remove a mark from map
    g_mapTestTaskRunning.remove(E_TT_SAMPLE_TEST, DEF_CUR_CHANNEL_SIZE);

    m_nIapTesting = -2;

    // Disconnect signal-slot
    _joinSignalSlotFuns(false);

    // Cancel testing
    _refreshTestingInfoOnLabels(31);

    QTimer::singleShot(100, this, [=]{
        // After cancel the test, the buzzer sounds
        FirmwareBLL::getInstance(this)->debugBuzzer(2);
    });
}

void FormTesting::slotReadTemperature()
{
    CanBLL_T1C::getInstance(this)->getChannelTemperatureAsyn();
}

int FormTesting::_startupTest()
{
    qDebug() << Q_FUNC_INFO;

    m_nNewTestId = 0;
    m_nFlowCycleCnt = 0;
    m_nSamplePosFlags = 0xff;
    m_nIapTesting = 0;

    int nRetVal = 0;
    int nTimeConsuming = 0;
    ST_PROCESS_PARAMS stProcessParamsExt;

    // Check the range of values
    m_nExtractId= 0;
    int nKitType = ui->tabWidget_KitType->currentIndex();

    m_sProcedureName = ((0 == nKitType) ? ui->listView_Procedure->currentIndex() : ui->listView_Procedure_2->currentIndex()).data().toString();
    if(!m_sProcedureName.isEmpty()) {
        m_nExtractId = ((QStandardItemModel*)((0 == nKitType) ? ui->listView_Procedure->model() : ui->listView_Procedure_2->model()))->itemFromIndex(((0 == nKitType) ? ui->listView_Procedure->currentIndex() : ui->listView_Procedure_2->currentIndex()))->statusTip().trimmed().toInt();
    }

    if(0 >= m_nExtractId) {
        FormPromptWidget::getInstance(this->parentWidget())->setPromptInfo(tr("Prompt"), tr("Please select an extractor procedure"));

        nRetVal = -2;
        goto do_exit;
    }

    // Check the method of extracts
    if(0 < m_nExtractId) {
        DatabaseBLL_T1C::getInstance(this)->selectProcessParamsById(stProcessParamsExt, m_nExtractId, E_PT_EXTRACT);
        if(0 >= stProcessParamsExt.nProcessId) {
            FormPromptWidget::getInstance(this->parentWidget())->setPromptInfo(tr("Prompt"), tr("Extract method is invalid"));

            nRetVal = -5;
            goto do_exit;
        }
    }

    if(g_mapTestTaskRunning.contains(E_TT_PROCESS_DEBUG)) {
        FormPromptWidget::getInstance(this)->setPromptInfo(tr("Prompt"), tr("Process testing in progress, please test later"));

        nRetVal = -7;
        goto do_exit;
    }
    else if(g_mapTestTaskRunning.contains(E_TT_HEATING_DEBUG)) {
        FormPromptWidget::getInstance(this)->setPromptInfo(tr("Prompt"), tr("heating testing in progress, please test later"));

        nRetVal = -8;
        goto do_exit;
    }
    else if(g_mapTestTaskRunning.contains(E_TT_PCR_DEBUG)) {
        FormPromptWidget::getInstance(this)->setPromptInfo(tr("Prompt"), tr("PCR testing in progress, please test later"));

        nRetVal = -9;
        goto do_exit;
    }
    else if(g_mapTestTaskRunning.contains(E_TT_INFUSE_DEBUG)) {
        FormPromptWidget::getInstance(this)->setPromptInfo(tr("Prompt"), tr("infuse testing in progress, please test later"));

        nRetVal = -10;
        goto do_exit;
    }

    if(1 == 1) {
        // Connect signal-slots
        int nErrCode = _joinSignalSlotFuns(true);
        if(0 != nErrCode) {
            FormPromptWidget::getInstance(this)->setPromptInfo(tr("Prompt"), tr("Failed to initialize signals") + QString("[%1]").arg(nErrCode));
            nRetVal = -90;

            goto do_exit;
        }
    }

    // Reset the images of testing status
    _resetImages(ui->tabWidget_KitType->currentIndex());

    if(0 < m_nExtractId) {
        gs_queFlowProcAction.clear();
        gs_lstFlowModelData.clear();

        int ActionSize = _splitProcedureAction(gs_queFlowProcAction, gs_lstFlowModelData, m_nFlowCycleCnt, stProcessParamsExt.sProcessContent);
        if(0 >= ActionSize) {
            nRetVal = -100;
            goto do_exit;
        }
        else {
            m_nFlowCycleCnt = gs_queFlowProcAction.constLast().nStepIdx + 1;
        }

        nTimeConsuming += stProcessParamsExt.nTimeConsume;

        // Connect signal-slots
        int nErrCode = _joinSignalSlotFuns(true);
        if(0 != nErrCode) {
            FormPromptWidget::getInstance(this)->setPromptInfo(tr("Prompt"), tr("Failed to initialize signals") + QString("[%1]").arg(nErrCode));
        }

        // Detect card exist or not
//        nRetVal = CanBLL_T1C::getInstance(this)->detectCardEx(0, nKitType);
        nRetVal = CanBLL_T1C::getInstance(this)->switchTray(false);
#ifndef QT_NO_DEBUG
        nRetVal = 2;
#endif
        qDebug() << Q_FUNC_INFO << nRetVal << g_nKitType << nKitType;

        bool bGoodKit = false;
        if(0 > nRetVal) {
            QString sInfo = "";
            switch (nRetVal) {
            case -101:{ sInfo = QString("%1[%2]").arg(tr("The types of card boxes are different")).arg(nRetVal); break; }
            case -102:{ sInfo = QString("%1[%2]").arg(tr("Card box not placed")).arg(nRetVal); break; }
            case -201:{ sInfo = QString("%1[%2]").arg(tr("Incorrect placement of magnetic sleeve")).arg(nRetVal); break; }
            case -202:{ sInfo = QString("%1[%2]").arg(tr("Pipe not placed")).arg(nRetVal); break; }
            case -203:{ sInfo = QString("%1[%2]").arg(tr("Tips not placed")).arg(nRetVal); break; }
            case -204:{ sInfo = QString("%1[%2]").arg(tr("PCR tube too high")).arg(nRetVal); break; }
            default: { sInfo = QString("%1[%2]").arg(tr("Failed to eject the tray")).arg(nRetVal); break; }
            }

            FormPromptWidget::getInstance(this->parentWidget())->setPromptInfo(tr("Prompt"), sInfo);
        }
        else if(1 == nRetVal || 2 == nRetVal) {
            g_nKitType = nRetVal;

            if(nKitType + 1 != nRetVal) {
                QString sErrInfo = QString("%1").arg(tr("Card box mismatch"));
                FormPromptWidget::getInstance(this)->setPromptInfo(tr("Prompt"), sErrInfo, true);
            }
            else {
                bGoodKit = true;
            }
        }
        else if(3 == nRetVal) {
            if(0 > g_nKitType) {
                QString sInfo = "";
                switch (g_nKitType) {
                case -101:{ sInfo = QString("%1[%2]").arg(tr("The types of card boxes are different")).arg(nRetVal); break; }
                case -102:{ sInfo = QString("%1[%2]").arg(tr("Card box not placed")).arg(nRetVal); break; }
                case -201:{ sInfo = QString("%1[%2]").arg(tr("Incorrect placement of magnetic sleeve")).arg(nRetVal); break; }
                case -202:{ sInfo = QString("%1[%2]").arg(tr("Pipe not placed")).arg(nRetVal); break; }
                case -203:{ sInfo = QString("%1[%2]").arg(tr("Tips not placed")).arg(nRetVal); break; }
                case -204:{ sInfo = QString("%1[%2]").arg(tr("PCR tube too high")).arg(nRetVal); break; }
                default: { sInfo = QString("%1[%2]").arg(tr("Failed to eject the tray")).arg(nRetVal); break; }
                }

                FormPromptWidget::getInstance(this->parentWidget())->setPromptInfo(tr("Prompt"), sInfo);
            }
            else {
                if(nKitType + 1 != g_nKitType) {
                    QString sErrInfo = QString("%1").arg(tr("Card box mismatch"));
                    FormPromptWidget::getInstance(this)->setPromptInfo(tr("Prompt"), sErrInfo, true);
                }
                else {
                    bGoodKit = true;
                }
            }
        }
        else {
            QString sErrInfo = QString("%1[%2]").arg(tr("Incorrect placement of the card box")).arg(-nRetVal);
            FormPromptWidget::getInstance(this)->setPromptInfo(tr("Prompt"), sErrInfo, true);
        }

        if(!bGoodKit) {
            // Disconnect signal-slots
            _joinSignalSlotFuns(false);

            if(tr("Inject") == ui->pushButton_SwitchTray->text()) {
                gs_nBtnTrayState = BTS_EJECT;
                ui->pushButton_SwitchTray->setText(tr("Eject"));
            }

            goto do_exit;
        }

        // Startup a process test
        nRetVal = CanBLL_T1C::getInstance(this)->startProcessTestingEx(gs_queFlowProcAction);
#ifndef QT_NO_DEBUG
        nRetVal = 0;
#endif

        if(0 != nRetVal) {
            FormPromptWidget::getInstance(this)->setPromptInfo(tr("Prompt"), tr("Failed to startup extract testing"));

            // Disconnect signal-slots
            _joinSignalSlotFuns(false);

            if(tr("Inject") == ui->pushButton_SwitchTray->text()) {
                gs_nBtnTrayState = BTS_EJECT;
                ui->pushButton_SwitchTray->setText(tr("Eject"));
            }

            // Mark the reaction holes under testing
            emit signalDrawHolePosition(E_AT_EXTRACT, 0, -1, m_nSamplePosFlags, QColor(255, 0, 0));
            emit signalDrawHolePosition(E_AT_SPECIMEN, nKitType, 0, m_nSamplePosFlags, QColor(255, 0, 0));

            nRetVal += -200;
            goto do_exit;
        }
        else {
            m_nIapTesting = 1;
            m_tTimeStamp = QDateTime::currentDateTime();
        }
    }

    // Insert a task
    g_mapTestTaskRunning.insert(E_TT_SAMPLE_TEST, DEF_CUR_CHANNEL_SIZE);

    if(0 != nRetVal) {
        nRetVal += -1000;
        goto do_exit;
    }
    else {
        gs_nBtnTestState = BTS_STOP;

        if(tr("Inject") == ui->pushButton_SwitchTray->text()) {
            gs_nBtnTrayState = BTS_EJECT;
            ui->pushButton_SwitchTray->setText(tr("Eject"));
        }

        ui->stackedWidget_Info->setCurrentIndex(1);
        ui->pushButton_NewTest->setText(tr("Stop Test"));
        ui->pushButton_Return->setVisible(false);
        ui->pushButton_SwitchTray->setEnabled(false);

        if(0 < m_nFlowCycleCnt) {
            // Startup an extract testing
            _refreshTestingInfoOnLabels(10);
        }
        else {
            // Startup a PCR testing
            _refreshTestingInfoOnLabels(11);
        }

        // Starup the progress bar
        emit m_pFrmProgressbar->signalRunTest(true, nTimeConsuming, E_PS_RUNNING);

        if(m_pReadTemperature->isActive()) m_pReadTemperature->stop();
        m_pReadTemperature->start();
    }

do_exit:
    QThread::msleep(5000);
    qDebug() << Q_FUNC_INFO << nRetVal;
    return nRetVal;
}

int FormTesting::_stopTest()
{
    int nRetVal = 0;

    gs_nBtnTestState = BTS_NEW;

    ui->pushButton_NewTest->setText(tr("New Test"));
    ui->pushButton_Return->setVisible(false);
    ui->pushButton_ViewProcedure->setVisible(false);

    if(m_pReadTemperature->isActive()) m_pReadTemperature->stop();
    if(m_pStepCountdown->isActive()) m_pStepCountdown->stop();

    int nWaitingForCanceling = DEF_RESET_TIME_CONSUME;
#ifndef QT_NO_DEBUG
    nWaitingForCanceling = 10;
#endif
    emit m_pFrmProgressbar->signalRunTest(true, nWaitingForCanceling, E_PS_STOPPING);

    if(0 < m_nIapTesting) {
        m_nIapTesting = -1;

        // Get extract flows & motor states
        ST_MOTOR_STATE stMotorState;
        CanBLL_T1C::getInstance(this)->getCurMotorState(stMotorState);
        UtilityAPI::getInstance(this)->splitProcedureAction(gs_queFlowProcAction, stMotorState);

        // Stop extract testing
        CanBLL_T1C::getInstance(this)->stopProcessTestingEx(gs_queFlowProcAction, stMotorState);

        FormPromptWidget::getInstance(this)->setPromptInfo(tr("Prompt"), tr("Canceling test, please wait"), false, nWaitingForCanceling * DEF_SEC_INTERVAL);
        m_pCancelTesting->start(nWaitingForCanceling * DEF_SEC_INTERVAL);
    }
    else {
        // Disconnect event-slots
        _joinSignalSlotFuns(false);
    }

    ui->pushButton_SwitchTray->setEnabled(true);
    m_pStepCountdown->stop();

    // Cancel testing
//    _refreshTestingInfoOnLabels(31);

    // Reset the images of testing status
    _resetImages(ui->tabWidget_KitType->currentIndex());

    qDebug() << Q_FUNC_INFO << nRetVal;
    return nRetVal;
}

void FormTesting::_completeTesting(bool bPCR)
{
    qDebug() << Q_FUNC_INFO << bPCR;

    gs_nBtnTestState = BTS_NEW;

    ui->pushButton_NewTest->setText(tr("New Test"));
    ui->pushButton_SwitchTray->setEnabled(true);

    m_pStepCountdown->stop();
    if(m_pReadTemperature->isActive()) m_pReadTemperature->stop();

    // Remove a mark from map
    g_mapTestTaskRunning.remove(E_TT_SAMPLE_TEST, DEF_CUR_CHANNEL_SIZE);

    // Disconnect signal-slots
    _joinSignalSlotFuns(false);

    // Stop the progress bar
    emit m_pFrmProgressbar->signalRunTest(false, 0, E_PS_IDLE);

    // Reset the images of testing status
    _resetImages(ui->tabWidget_KitType->currentIndex());

    // Complete the extract testing
    if(!bPCR) {
        _refreshTestingInfoOnLabels(20);
    }
    else {
        // Prevent the button from clicking repeatedly quickly
        FormShieldDlg::getInstance(this)->show();
        emit m_pFrmProgressbar->signalRunTest(true, 3, E_PS_PROCESSING);

        _refreshTestingInfoOnLabels(30);

        emit m_pFrmProgressbar->signalRunTest(false, 0, E_PS_COMPLITED);

        FormPromptWidget::getInstance(this)->hideWindow();
        FormShieldDlg::getInstance(this)->hide();
    }

    QTimer::singleShot(1000, this, [=]{
        // After completing the test, the buzzer sounds
        FirmwareBLL::getInstance(this)->debugBuzzer(1);
    });
}

int FormTesting::_joinSignalSlotFuns(bool bConnect)
{
    qDebug() << Q_FUNC_INFO << bConnect;

    int nRetVal = 0;

    if(bConnect) {
        // Disconnect signal-slot
        _joinSignalSlotFuns(false);

        // Signal-slot initialization daynamic
        std::vector<bool> vecConnect;

        // Extract
        vecConnect.push_back(QObject::connect(CanBLL_T1C::getInstance(this), SIGNAL(signalProcessTestCycles(unsigned int, ST_FLOW_PROC_ACTION)), this, SLOT(slotProcessTestCycles(unsigned int, ST_FLOW_PROC_ACTION)), static_cast<Qt::ConnectionType>(Qt::AutoConnection | Qt::UniqueConnection)));
        vecConnect.push_back(QObject::connect(CanBLL_T1C::getInstance(this), SIGNAL(signalHeatingPayload(unsigned int, QList<float>)), this, SLOT(slotHeatValueReceived(unsigned int, QList<float>)), static_cast<Qt::ConnectionType>(Qt::AutoConnection | Qt::UniqueConnection)));

        for(int i=0; i<(int)vecConnect.size(); i++) {
            if(!vecConnect.at(i)) {
                nRetVal = -(i + 1);
                break;
            }
        }
    }
    else {
        // Disconnect signal-slot

        // Extract
        QObject::disconnect(CanBLL_T1C::getInstance(this), SIGNAL(signalProcessTestCycles(unsigned int, ST_FLOW_PROC_ACTION)), this, SLOT(slotProcessTestCycles(unsigned int, ST_FLOW_PROC_ACTION)));
        QObject::disconnect(CanBLL_T1C::getInstance(this), SIGNAL(signalHeatingPayload(unsigned int, QList<float>)), this, SLOT(slotHeatValueReceived(unsigned int, QList<float>)));
    }

    qDebug() << Q_FUNC_INFO << nRetVal;
    return nRetVal;
}

int FormTesting::_splitProcedureAction(QQueue<ST_FLOW_PROC_ACTION> &queFlowProcAction, QList<ST_STEP_INFO> &lstModelData, int &nCycleCnt, const QString &sProcessContent)
{
    qDebug() << Q_FUNC_INFO << sProcessContent;
    queFlowProcAction.clear();
    lstModelData.clear();
    nCycleCnt = 0;

    int nRetVal = 0;

    int nMedelSize = UtilityAPI::getInstance()->getExtractStepInfo(lstModelData, sProcessContent);
    if(0 < nMedelSize) {
        // The cycles of extract procedure
        nCycleCnt = nMedelSize;

        int nKitType = ui->tabWidget_KitType->currentIndex();
        int nActionSize = UtilityAPI::getInstance()->splitProcedureAction(queFlowProcAction, lstModelData, nKitType);
        if(0 < nActionSize) {
            nRetVal = nActionSize;
        }
        else {
            nRetVal = -1;
        }
    }

    qDebug() << Q_FUNC_INFO << nRetVal;
    return nRetVal;
}

QString FormTesting::_seconds2Time(int nSeconds)
{
    QString sTime = "";
    int nHours = 0;
    int nMinutes = 0;

    nHours= nSeconds / 3600;
    nSeconds %= 3600;
    nMinutes = nSeconds / 60;
    nSeconds %= 60;

    sTime = QString("%1").arg(nHours, 2, 10, QLatin1Char('0'))
            + ":"
            + QString("%1").arg(nMinutes, 2, 10, QLatin1Char('0'))
            + ":"
            + QString("%1").arg(nSeconds, 2, 10, QLatin1Char('0'));

//    qDebug() << Q_FUNC_INFO << nSeconds << sTime;
    return sTime;
}

void FormTesting::_refreshTestingInfoOnLabels(int nStatus)
{
    qDebug() << Q_FUNC_INFO << nStatus;

    switch (nStatus) {
    case 0: // Initialize labels
        gs_sTestingName = "";
        gs_sStepProgress = "--";
        gs_sStepName = "--";
        gs_sStepDetail = "";
        gs_sHoleNumber = "--";
        gs_sStillTime = "00:00:00";
        gs_sMixingTime = "00:00:00";
        gs_sMagnetTime = "00:00:00";
        gs_sStillTime2 = "";
        gs_sMixingTime2 = "";
        gs_sMagnetTime2 = "";
        gs_sTempValHole = "--";
        gs_sTempValHole1 = "--";
        gs_sTempValHole2 = "--";
        gs_sTempValHole3 = "--";
        gs_sTempValHole4 = "--";
        gs_nBtnResultState = BRS_NONE;

        ui->progressBar->setVisible(false);
        ui->stackedWidget_Progress->setCurrentIndex(0);
        break;
    case 1: // Ready an extract testing
        gs_sTestingName = "";
        gs_sStepProgress = "";
        gs_sStepName = "";
        gs_sStepDetail = tr("Ready extract testing");
        gs_sHoleNumber = "";
        gs_sStillTime = "";
        gs_sMixingTime = "";
        gs_sMagnetTime = "";
        gs_sStillTime2 = "";
        gs_sMixingTime2 = "";
        gs_sMagnetTime2 = "";
        gs_sTempValHole = "";
        gs_sTempValHole1 = "";
        gs_sTempValHole2 = "";
        gs_sTempValHole3 = "";
        gs_sTempValHole4 = "";
        gs_nBtnResultState = BRS_NONE;

        ui->progressBar->setVisible(false);
        ui->stackedWidget_Progress->setCurrentIndex(0);
        break;
    case 2: // Ready a PCR testing
        gs_sTestingName = "";
        gs_sStepProgress = "";
        gs_sStepName = "";
        gs_sStepDetail = tr("Ready PCR testing");
        gs_sHoleNumber = "";
        gs_sStillTime = "";
        gs_sMixingTime = "";
        gs_sMagnetTime = "";
        gs_sStillTime2 = "";
        gs_sMixingTime2 = "";
        gs_sMagnetTime2 = "";
        gs_sTempValHole = "";
        gs_sTempValHole1 = "";
        gs_sTempValHole2 = "";
        gs_sTempValHole3 = "";
        gs_sTempValHole4 = "";
        gs_nBtnResultState = BRS_NONE;

        ui->progressBar->setVisible(false);
        ui->stackedWidget_Progress->setCurrentIndex(0);
        break;
    case 10: // Startup an extract testing
        gs_sTestingName = m_sProcedureName.isEmpty() ? "" : tr("Current testing") + ": " + m_sProcedureName;
        gs_sStepProgress = "";
        gs_sStepName = "";
        gs_sStepDetail = tr("Startup extract testing");
        gs_sHoleNumber = "";
        gs_sStillTime = "";
        gs_sMixingTime = "";
        gs_sMagnetTime = "";
        gs_sStillTime2 = "";
        gs_sMixingTime2 = "";
        gs_sMagnetTime2 = "";
        gs_sTempValHole = "";
        gs_sTempValHole1 = "";
        gs_sTempValHole2 = "";
        gs_sTempValHole3 = "";
        gs_sTempValHole4 = "";
        gs_nBtnResultState = BRS_NONE;

        ui->progressBar->setValue(0);
        ui->progressBar->setRange(0, gs_queFlowProcAction.size());
        ui->progressBar->setVisible(true);
        ui->stackedWidget_Progress->setCurrentIndex(0);
        break;
    case 11: // Startup a PCR testing
        gs_sTestingName = m_sProcedureName.isEmpty() ? "" : tr("Current testing") + ": " + m_sProcedureName;
        gs_sStepProgress = "";
        gs_sStepName = "";
        gs_sStepDetail = tr("Heating lid cover");
        gs_sHoleNumber = "";
        gs_sStillTime = "";
        gs_sMixingTime = "";
        gs_sMagnetTime = "";
        gs_sStillTime2 = "";
        gs_sMixingTime2 = "";
        gs_sMagnetTime2 = "";
        gs_sTempValHole = "";
        gs_sTempValHole1 = "";
        gs_sTempValHole2 = "";
        gs_sTempValHole3 = "";
        gs_sTempValHole4 = "";
        gs_nBtnResultState = BRS_NONE;

        ui->progressBar->setValue(0);
        ui->progressBar->setRange(0, gs_quePcrProcAction.size());
        ui->progressBar->setVisible(true);
        ui->stackedWidget_Progress->setCurrentIndex(0);
        break;
    case 20: // Complete extract testing
        gs_sTestingName = "";
        gs_sStepProgress = "";
        gs_sStepName = "";
        gs_sStepDetail = tr("Please exit the warehouse");
        gs_sHoleNumber = "";
        gs_sStillTime = "";
        gs_sMixingTime = "";
        gs_sMagnetTime = "";
        gs_sStillTime2 = "";
        gs_sMixingTime2 = "";
        gs_sMagnetTime2 = "";
        gs_sTempValHole = "";
        gs_sTempValHole1 = "";
        gs_sTempValHole2 = "";
        gs_sTempValHole3 = "";
        gs_sTempValHole4 = "";
        gs_nBtnResultState = BRS_COMPLITED;

        ui->progressBar->setVisible(false);
        ui->stackedWidget_Progress->setCurrentIndex(1);
        break;
    case 30: // Complete PCR testing
        gs_sTestingName = "";
        gs_sStepProgress = "";
        gs_sStepName = "";
        gs_sStepDetail = tr("Complete testing");
        gs_sHoleNumber = "";
        gs_sStillTime = "";
        gs_sMixingTime = "";
        gs_sMagnetTime = "";
        gs_sStillTime2 = "";
        gs_sMixingTime2 = "";
        gs_sMagnetTime2 = "";
        gs_sTempValHole = "";
        gs_sTempValHole1 = "";
        gs_sTempValHole2 = "";
        gs_sTempValHole3 = "";
        gs_sTempValHole4 = "";
        gs_nBtnResultState = BRS_COMPLITED;

        ui->progressBar->setVisible(false);
        ui->stackedWidget_Progress->setCurrentIndex(1);
        break;
    case 31: // Cancel testing
        gs_sTestingName = "";
        gs_sStepProgress = "";
        gs_sStepName = "";
        gs_sStepDetail = tr("Please exit the warehouse");
        gs_sHoleNumber = "";
        gs_sStillTime = "";
        gs_sMixingTime = "";
        gs_sMagnetTime = "";
        gs_sStillTime2 = "";
        gs_sMixingTime2 = "";
        gs_sMagnetTime2 = "";
        gs_sTempValHole = "";
        gs_sTempValHole1 = "";
        gs_sTempValHole2 = "";
        gs_sTempValHole3 = "";
        gs_sTempValHole4 = "";
        gs_nBtnResultState = BRS_CANCELED;

        ui->progressBar->setVisible(false);
        ui->stackedWidget_Progress->setCurrentIndex(1);
        break;
    default:
        break;
    }

    ui->label_TestingProgress->setText(gs_sTestingName);
    ui->label_StepProgress_2->setText(gs_sStepProgress);
    ui->label_StepName_2->setText(gs_sStepName);
    ui->label_StepDetail_2->setText(gs_sStepDetail);
    ui->label_HoleNumber_2->setText(gs_sHoleNumber);
    ui->label_StillTime_2->setText(gs_sStillTime);
    ui->label_StillTime_3->setText(gs_sStillTime2);
    ui->label_MixingTime_2->setText(gs_sMixingTime);
    ui->label_MixingTime_3->setText(gs_sMixingTime2);
    ui->label_MagnetTime_2->setText(gs_sMagnetTime);
    ui->label_MagnetTime_3->setText(gs_sMagnetTime2);
    ui->label_HoleTemperature_2->setText(gs_sTempValHole);
    ui->label_Hole1_2->setText(gs_sTempValHole1);
    ui->label_Hole2_2->setText(gs_sTempValHole2);
    ui->label_Hole3_2->setText(gs_sTempValHole3);
    ui->label_Hole4_2->setText(gs_sTempValHole4);

    switch (gs_nBtnResultState) {
    case BRS_COMPLITED: { ui->pushButton_ResultInfo->setText(tr("Test completed")); break; }
    case BRS_CANCELED: { ui->pushButton_ResultInfo->setText(tr("Test canceled")); break; }
    default: { ui->pushButton_ResultInfo->setText(""); break; }
    }
}

/*
 * Function:    _refreshListView
 * Description: Refresh the list view data
 * Paramer:     void
 * Return:      void
 */
void FormTesting::_refreshListView()
{
    int nRetVal = 0;

    // Get the list of process names (10 holes)
    if(1 == 1) {
        QMap<int, QString> mapProcessName;
        nRetVal = DatabaseBLL_T1C::getInstance()->selectProcessNameList(mapProcessName, E_PT_EXTRACT, E_EK_T1C_HOLE_10);
        if(0 < nRetVal) {
            QStandardItemModel *pModel = new QStandardItemModel(this);
            QStringList lstProcessName;

            lstProcessName.clear();
            QMapIterator<int, QString> itor(mapProcessName);
            while (itor.hasNext()) {
                itor.next();

                QStandardItem *pItem = new QStandardItem(QIcon(DEF_STEP_ICON_FILE2), itor.value());
                // Cache the process ID
                pItem->setStatusTip(QString::number(itor.key()));
                pModel->appendRow(pItem);
            }

            ui->listView_Procedure->setModel(pModel);
            ui->listView_Procedure->setViewMode(QListView::IconMode);
            ui->listView_Procedure->setIconSize(QSize(48, 48));
            ui->listView_Procedure->setGridSize(QSize(140, 84));
            ui->listView_Procedure->setMovement(QListView::Static);
            ui->listView_Procedure->setSelectionMode(QAbstractItemView::SingleSelection);
            ui->listView_Procedure->setEditTriggers(QAbstractItemView::NoEditTriggers);
            ui->listView_Procedure->setFlow(QListView::LeftToRight);
            ui->listView_Procedure->setFocusPolicy(Qt::NoFocus);
            ui->listView_Procedure->scrollToTop();
        }
    }
    // Get the list of process names (11 holes)
    if(2 == 2) {
        QMap<int, QString> mapProcessName;
        nRetVal = DatabaseBLL_T1C::getInstance()->selectProcessNameList(mapProcessName, E_PT_EXTRACT, E_EK_T1C_HOLE_11);
        if(0 < nRetVal) {
            QStandardItemModel *pModel = new QStandardItemModel(this);
            QStringList lstProcessName;

            lstProcessName.clear();
            QMapIterator<int, QString> itor(mapProcessName);
            while (itor.hasNext()) {
                itor.next();

                QStandardItem *pItem = new QStandardItem(QIcon(DEF_STEP_ICON_FILE3), itor.value());
                // Cache the process ID
                pItem->setStatusTip(QString::number(itor.key()));
                pModel->appendRow(pItem);
            }

            ui->listView_Procedure_2->setModel(pModel);
            ui->listView_Procedure_2->setViewMode(QListView::IconMode);
            ui->listView_Procedure_2->setIconSize(QSize(48, 48));
            ui->listView_Procedure_2->setGridSize(QSize(140, 84));
            ui->listView_Procedure_2->setMovement(QListView::Static);
            ui->listView_Procedure_2->setSelectionMode(QAbstractItemView::SingleSelection);
            ui->listView_Procedure_2->setEditTriggers(QAbstractItemView::NoEditTriggers);
            ui->listView_Procedure_2->setFlow(QListView::LeftToRight);
            ui->listView_Procedure_2->setFocusPolicy(Qt::NoFocus);
            ui->listView_Procedure_2->scrollToTop();
        }
    }

}

void FormTesting::_loadExtractImage(int nKitType)
{
    qDebug() << Q_FUNC_INFO << nKitType;

    if(m_pImageExtract) m_pImageExtract->load((E_EK_T1C_HOLE_10 == nKitType) ? ":/images/home/testing/extract_10.png" : ":/images/home/testing/extract_11.png");
    ui->label_Animation_Extract->setPixmap(QPixmap::fromImage(*m_pImageExtract).scaled(ui->label_Animation_Extract->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
}

void FormTesting::_resetImages(int nKitType)
{
    qDebug() << Q_FUNC_INFO << nKitType;

    emit signalDrawHolePosition(E_AT_EXTRACT, nKitType, -1, m_nSamplePosFlags, QColor(255, 255, 255));
    emit signalDrawHolePosition(E_AT_SPECIMEN, 0, 0, m_nSamplePosFlags, QColor(255, 255, 255));
}
